# -*- coding: utf-8 -*-
# @version        : 1.0
# @Creaet Time    : 2021/9/29 23:03 
# @File           : case01.py
# @IDE            : PyCharm
# @desc           : 案例01，请求操作


"""
请求操作：
路径参数，路径顺序，枚举类型参数，文件路径类型参数，路径参数验证，定义查询参数，bool类型转换
查询参数验证，定义请求体与验证，多种参数混合，请求体嵌套

额外数据类型：https://fastapi.tiangolo.com/zh/tutorial/extra-data-types/
"""

"""
七种类型参数：
路径参数：fastapi.Path
查询参数：fastapi.Query
请求体参数：pydantic.BaseModel（pydantic.Field），请求体中不能添加路径参数（Path） 与 查询参数（Query）
Header参数：fastapi.Header，fastapi.Cookie
Request参数：fastapi.Request
文件参数：fastapi.File，fastapi.UploadFile
表单参数：fastapi.Form
"""

from datetime import datetime
from enum import Enum
from fastapi import APIRouter, Path, Query
from typing import List
from pydantic import BaseModel, Field

case01 = APIRouter()


@case01.get("/", summary="案例1初始接口")
def case01_root():
    return "case01_root"


@case01.get("/user/{id}", summary="路径参数")
def get_user(id: int):
    """
    路径参数id的值会直接作为实参id传递给函数get_user。<br>
    并指定了id参数类型为int整数，如果传入的参数不为整数，那么会报错。<br>
    注意：如果传入的参数为"3"，那么会将字符串3自动转换为整数3<br>
    也就是说，借助类型声明，FastAPI可以对Request内容自动进行数据解析和数据转换。<br>
    """
    return {"id": id}


@case01.get("/path/params/validate/{num}", summary="路径参数验证")
def path_params_validate(
        num: int = Path(..., title="整数字段", description="大于1，小于10", ge=1, le=10),
):
    """
    使用fastapi.Path函数可以自定义路径参数验证规则
    """
    return {"num": num}


@case01.get("/user/admin", summary="路径顺序，固定路径")
def hello_user():
    """
    如果存在固定路径与参数路径可能发生一致的情况，并且他们的请求方法一致 <br>
    那么应该确保固定路径应该放在参数路径前面 <br>
    否则将一直无法访问到固定路径接口函数
    """
    return "hello admin"


@case01.get("/user/{name}", summary="路径顺序，参数路径")
def welcome_user(name: str):
    return f"welcome {name}"  # Python3.6 版本开始出现了此新的格式化字符串语法，性能提升


# 定义性别枚举类
class Sex(str, Enum):
    man = "man"
    woman = "woman"


@case01.get("/sex/{sex}", summary="枚举类型参数")
def get_sex(sex: Sex):
    """
    定义枚举类型参数后，在接口文档中，<br>
    在传递这个参数时，会显示选择框，选择项为定义的枚举类属性值
    如果传递的参数值不为枚举类中的数据值，那么就会报错：“value”不是有效的枚举成员
    """
    return {"sex": sex}


@case01.get("/file/{filepath: path}", summary="文件路径类型参数")
def download_file(filepath: str):
    """
    在传递路径参数时，如果参数值为一个文件路径，例如：/usr/local/README.md<br>
    那么系统不会认为是这是一整个参数，而是会将它当做路由的一部分进行解析，则会报错："detail": "Not Found"<br>
    所以我们需要在这种文件路径参数后面指定为path类型，这样系统就会将后面这一部分解析为字符串
    """
    return f"download {filepath}"


@case01.get("/users/", summary="定义查询参数")
def get_users(page: int, limit: int = None):
    return {"page": page, "limit": limit}


@case01.get("/bool/type/conversion", summary="bool类型转换")
def bool_type_conversion(is_active: bool = False):
    """
    bool类型参数，在接口文档中同样会自动显示为选择框
    但是可以直接通过访问URL来测试：http://127.0.0.1:8000/study/case01/bool/type/conversion?is_active=false
    参数值中：yes，on，1，True，true会被自动转换为True
    """
    return {"is_active": is_active}


@case01.get("/query/params/validate", summary="定义查询参数")
def query_params_validate(
        page: int = Query(..., title="页码", description="大于1，小于10", ge=1, le=10),
        name: str = Query(..., title="名称", description="大于1，小于10", min_length=1, max_length=10),
        roles: List[str] = Query(default=["admin", "quest"], alias="角色列表", description="添加了别名，与默认值")
):
    return {"page": page, "name": name, "roles": roles}


class User(BaseModel):
    """
    定义请求体数据模型<br>
    请求体中不能添加路径参数（Path） 与 查询参数（Query）<br>
    注意：GET请求不支持传输请求体参数，只能使用POST, PUT, DELETE等<br>
    GET请求报错：TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
    """
    username: str = Field(..., title="用户名", min_length=1, max_length=20)
    password: str = Field(default="123456", title="密码", min_length=6, max_length=16)
    create_time: datetime = datetime.now()

    class Config:
        # 示例参数值，会默认显示在接口文档中
        schema_extra = {
            "example": {
                "username": "admin",
                "password": "admin123",
                "create_time": "2020-01-01 00:01:02"
            }
        }


@case01.post("/request/data/validate", summary="定义请求体参数并验证")
def request_data_validate(user: User):
    return {"username": user.username, "password": user.password, "create_time": user.create_time}


@case01.post("/params/validate/{id}", summary="多种参数混合")
def params_validate(
        id: int,
        user: User,
        page: int = Query(..., title="查询参数")
):
    return {"id": id,"username": user.username, "password": user.password, "create_time": user.create_time, "page": page}


class Users(BaseModel):
    users: List[User] = None  # 请求体嵌套
    create_user_id: int = Field(..., title="整数类型", ge=0)
    desc: str = None


@case01.post("/request/data/nesting", summary="请求体嵌套")
def request_data_nesting(users: Users):
    return {"create_user_id": users.create_user_id, "desc": users.desc, "user": users.users}